<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-eqv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>jsfx</title>
	<script>
		window.onerror = function(msg, url, linenumber) {
			alert('Error message: '+msg+'\nURL: '+url+'\nLine Number: '+linenumber);
			return true;
		}
	</script>
	<script src="jsfx.js"></script>
</head>
<body>
	<div id="jsfx">
		<h1 class="title">jsfx</h1>
		<div class="description">See <a href="https://github.com/loov/jsfx">github.com/loov/jsfx</a> for more information.</div>
		<div></div>
		<div id="control" class="panel">
			<div class="panel-title">Control</div>
			<input type="checkbox" id="use-audiocontext" title="Use AudioContext." onclick="ChangePlayMode()">
			<button onclick="PlayCurrent()">Play</button>
			<button onclick="AddToLibrary()">Add</button>
			<a class="button" id="download" href="#" onmousedown="UpdateDownloadLink()" download="sound.wav">Download</a>
			<div id="last-time" title="Time it took to generate."></div>
			<div class="clear"></div>
		</div>
		<div id="presets" class="panel">
			<div class="panel-title">Presets</div>
		</div>
		<div class="clear"></div>
		<div id="panels">
		</div>
		<div class="clear"></div>

		<div class="panel wide">
			<div class="panel-title">Library</div>
			<input type="text" readonly id="library-content" title="Paramset for the current Library.">
			<div id="library"></div>
		</div>
	</div>
<script>
"use strict";
var Library = {};
var CurrentParams = {};

function PlayCurrent(){ Play(CurrentParams); }

var PlayAudio = function(params){
	var start = (new Date())|0;
	jsfx.Sound(params).play();
	var stop = (new Date())|0;
	var time = (stop - start) + "ms"
	console.log("Generated in", time);
	document.getElementById("last-time").innerText = time;
};
var Play = PlayAudio;

var PlayContext;
if(typeof AudioContext !== "undefined"){
	var Live = jsfx.Live();
	PlayContext = function(params){
		Live._play(params);
	};
} else {
	document.getElementById("use-audiocontext").hidden = true;
}

function ChangePlayMode(){
	if(document.getElementById("use-audiocontext").checked){
		Play = PlayContext;
	} else {
		Play = PlayAudio;
	}
}

function AddToLibrary(){
	var name = prompt("Please enter name for the sound:");
	if((name == null) || (name == "")){
		return;
	}

	var params = JSON.parse(JSON.stringify(CurrentParams));
	jsfx._RemoveEmptyParams(params);

	Library[name] = params;
	UpdateHash();
	UpdateCurrentView();
}

function SelectPreset(preset){
	CurrentParams = preset();
	UpdateCurrentView();
	PlayCurrent();
}

var LastHash = "";
function UpdateHash(){
	var json = JSON.stringify(Library);
	document.getElementById("library-content").value = json;
	LastHash = "#" + btoa(json);
	window.location.hash = LastHash;
}

function LoadFromHash(){
	var json = window.location.hash.substr(1);
	try{
		json = atob(json)
		document.getElementById("library-content").value = json;
		Library = JSON.parse(json);
	}catch(e){ console.log(e); }
}

if(window.location.hash != ""){ LoadFromHash(); }
window.onhashchange = function(ev){
	if(window.location.hash != LastHash){
		LoadFromHash();
		UpdateCurrentView();
	}
};

function UpdateDownloadLink(){
	var sound = jsfx.Sound(CurrentParams);
	var el = document.getElementById("download");
	el.href = sound.src;
}

function UpdateCurrentView(){
	// create a copy of params
	var params = JSON.parse(JSON.stringify(CurrentParams));
	jsfx.InitDefaultParams(params, jsfx.DefaultModules);

	list(params, function(moduleName, params){
		list(params, function(paramName, paramValue){
			var element = document.getElementById(moduleName + "$" + paramName);;
			if(element){
				element.value = paramValue;
			}
			var element = document.getElementById(moduleName + "_" + paramName + "_" + paramValue);
			if(element){
				element.checked = true;
			}
		});
	});

	var library = document.getElementById("library");
	library.innerHTML = "";
	var els = list(Library, function(name, params){
		var load  = E("button", "item-load", "#");
		load.title = "Load";
		load.onclick = function(){
			CurrentParams = JSON.parse(JSON.stringify(params));
			UpdateCurrentView();
		};

		var del  = E("button", "item-delete", "X");
		del.title = "Delete";
		del.onclick = function(){
			delete Library[name];
			UpdateHash();
			UpdateCurrentView();
		}

		var el = E("div", "library-item", [
			load,
			E("span", "item-name", name),
			del
		]);
		el.title = "play";
		el.onclick = function(){
			Play(params);
		};
		library.appendChild(el);
	});
}

document.getElementById("library-content").click = function(ev){
	ev.currentTarget.select();
}

function ModifyValue(moduleName, paramName, newValue){
	// console.log("CHANGE", moduleName, paramName, newValue);
	if(CurrentParams[moduleName] == null){
		CurrentParams[moduleName] = {};
	}
	CurrentParams[moduleName][paramName] = newValue;
	PlayCurrent();
}

function CreateParam(name, def, module){
	var sel;
	if(def.C){
		sel = E("form", "", list(def.C, function(value){
			var input = E("input", "", []);
			input.type = "radio";
			input.id   = module.name + "_" + name + "_" + value;
			input.name  = module.name + "_" + name;
			input.value = value;
			input.onchange = function(ev){
				ModifyValue(module.name, name, input.value);
			};

			var label = E("label", "", value);
			label.htmlFor = input.id;
			return E("span", "radio-option", [input, label]);
		}));
	} else {
		sel = E("input", "", []);
		sel.type = "range";
		sel.min = def.L;
		sel.max = def.H;
		sel.value = def.D;
		sel.step = 0.01;
		sel.onchange = function(ev){
			ModifyValue(module.name, name, parseFloat(sel.value));
		};
		if(def.H - def.L > 10){
			sel.step = 1;
		}
	}
	sel.id = module.name + "$" + name;

	return E("tr", "", [
		E("td", "", name),
		E("td", "", [sel])
	]);
}

function CreatePanel(module){
	return E("div", "panel", [
		E("div", "panel-title", module.name),
		E("table", "", list(module.params, CreateParam, module))
	]);
}

function Add(module){
	document.getElementById("panels").appendChild(CreatePanel(module));
}

Add(jsfx.Module.Generator);
Add(jsfx.Module.Frequency);
Add(jsfx.Module.Volume);
Add(jsfx.Module.Vibrato);
Add(jsfx.Module.Filter);
Add(jsfx.Module.Phaser);

document.getElementById("panels").appendChild(E("div", "clear", []));

list(jsfx.Preset, function(name, preset){
	var button = E("button", "", name);
	button.onclick = function(){ SelectPreset(preset); };
	document.getElementById("presets").appendChild(button);
});
document.getElementById("presets").appendChild(E("div", "clear", []));

function E(tag, className, content){
	var el = document.createElement(tag);
	if(className !== ''){
		el.className = className;
	}
	if(typeof content == 'object'){
		for(var i = 0; i < content.length; i += 1){
			el.appendChild(content[i]);
		}
	} else if (typeof content == 'string'){
		el.innerHTML = content;
	}
	return el;
}

function list(obj, fn, X){
	var r = [];
	for(var name in obj){
		if(obj.hasOwnProperty(name)){
			r.push(fn(name, obj[name], X));
		}
	}
	return r;
}

UpdateCurrentView();
</script>
<style>
	* { margin: 0; padding: 0; box-sizing: border-box; }
	body, html {
		font-family: RobotoDraft,Roboto,Helvetica Neue,Helvetica,Arial,sans-serif;
		font-weight: 300;
		line-height: 1.2;
		color: #333333;
	}
	.clear { clear: both; }

	#jsfx {
		margin: 16px auto;
		max-width: 650px;
	}

	#jsfx .title {
		font-size: 2em;
		color: #444;

		clear: both;
	}

	#jsfx .description {
		margin-bottom: 8px;
	}

	#jsfx .panel {
		float: left;
		clear: left;

		position: relative;
		margin: 8px;
		padding: 4px;

		width: 300px;

		background: #FFF;
		box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);
	}

	#jsfx .panel:nth-child(2n) {
		float: right;
		clear: right;
	}

	#jsfx .panel.wide {
		float: none; clear: none;
		width: 100%;
	}

	@media(max-width: 650px){
		#jsfx {
			width: 100%;
			margin: 0;
		}
		#jsfx .panel {
			float: none !important;
			clear: none !important;
			margin: 8px 0;
			width: 100%;
		}
	}

	#jsfx .panel .panel-title {
		padding: 4px;
		font-size: 12px;
		color: #666;
	}

	#jsfx .panel button, #jsfx .panel .button {
		float: left;
	}

	#jsfx button, #jsfx .button {
		font-family: RobotoDraft,Roboto,Helvetica Neue,Helvetica,Arial,sans-serif;
		font-weight: 300;
		font-size: 14px;

		margin: 2px;
		border: 1px solid #AAA;
		background: #FFF;
		cursor: pointer;
		padding: 4px 8px;
		text-decoration: none;
		color: inherit;
	}

	#jsfx button:hover, #jsfx .button:hover {
		border: 1px solid #7DC6FF;
		box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37);
	}

	#jsfx .panel table {
		font-size: 0.8em;
		width: 100%;
	}

	#jsfx #use-audiocontext {
		float: left;
	}

	#jsfx .panel table input[type="range"] { width: 100%; }
	#jsfx .panel table tr td:first-child   { width: 80px; }

	#jsfx .panel .radio-option {
		display: inline-block;
		margin-left: 2px;
		line-height: 10px;
		border: 1px solid #eee;
		margin: 2px 4px;
		padding: 2px 4px;
		width: 40%;
	}

	#jsfx .panel .radio-option input {
		height: 10px;
	}

	#jsfx #last-time {
		position: absolute;
		bottom: 4px;
		right: 8px;
	}

	#jsfx #library-content {
		position: absolute;
		top: 4px;
		right: 4px;
	}
	#jsfx #library .library-item {
		position: relative;
		cursor: pointer;
		line-height: 24px;
	}
	#jsfx #library .library-item:hover {
		background: #eee;
	}

	#jsfx #library .library-item .item-name {
		margin-left: 8px;
		vertical-align: middle;
		font-size: 14px;
	}

	#jsfx #library .library-item button {
		padding: 0px 4px;
		width: 32px;
		text-align: center;
	}
	#jsfx #library .library-item .item-delete {
		display: block;
		position: absolute;
		right: 1px; bottom: 1px; top: 1px;

		border: 1px solid #F88;
	}
</style>
</body>
</html>